require("FakeBitOps")

sha1={}
sha1["bitwise_blocksize"] = 512
sha1["blocksize"] = 64

function sha1.computehash(s,raw_mode)
	--Init
	local message = s
	if not (raw_mode == true) then
		message = String2Bin(s)
	end
	local mlbpp = Number2Bin(#message) -bzero- 64
	--Wondering if storing the actual bitstring is any faster than generating it with Number2Bin?
	local h0 = "01100111010001010010001100000001" --Number2Bin(0x67452301)
	local h1 = "11101111110011011010101110001001" --Number2Bin(0xEFCDAB89)
	local h2 = "10011000101110101101110011111110" --Number2Bin(0x98BADCFE)
	local h3 = "00010000001100100101010001110110" --Number2Bin(0x10325476)
	local h4 = "11000011110100101110000111110000" --Number2Bin(0xC3D2E1F0)
	--Preprocessing
	message = message.."1"
	message = ZeroPad(message,#message+512-(#message%512)-64,Direction.RIGHT)..mlbpp
	--512 Chunks
	for chunk in message:gmatch(string.rep("[01]",512)) do
		w = {}
		local i = 0
		for c in chunk:gmatch(string.rep("[01]",32)) do
			w[i] = c
			i = i+1
		end
		for i = 16,79 do
			w[i] = w[i-3] -bxor- w[i-8] -bxor- w[i-14] -bxor- w[i-16] -brot- 1
		end
		--init hash values for this chunk
		local a=h0
		local b=h1
		local c=h2
		local d=h3
		local e=h4
		for i = 0,79 do
			if i <= 19 then
				f = (b -band- c) -bor- (Not(b) -band- d)
				k = "01011010100000100111100110011001" --Number2Bin(0x5A827999)
			elseif i <= 39 then
				f = b -bxor- c -bxor- d
				k = "01101110110110011110101110100001" --Number2Bin(0x6ED9EBA1)
			elseif i <= 59 then
				f = (b -band- c) -bor- (b -band- d) -bor- (c -band- d)
				k = "10001111000110111011110011011100" --Number2Bin(0x8F1BBCDC)
			elseif i <= 79 then
				f = b -bxor- c -bxor- d
				k = "11001010011000101100000111010110" --Number2Bin(0xCA62C1D6)
			end
			local temp = a -brot- 5 -badd- f -badd- e -badd- k -badd- w[i]
			-- If higher than 32 bits, truncate the left.
			if #temp > 32 then
				temp = string.sub(temp,(#temp-32)+1,#temp)
			end
			e = d
			d = c
			c = b -brot- 30
			b = a
			a = temp
		end
		h0 = h0 -badd- a
		-- If higher than 32 bits, truncate the left.
		if #h0 > 32 then
			h0 = string.sub(h0,(#h0-32)+1,#h0)
		end
		h1 = h1 -badd- b
		-- If higher than 32 bits, truncate the left.
		if #h1 > 32 then
			h1 = string.sub(h1,(#h1-32)+1,#h1)
		end
		h2 = h2 -badd- c
		-- If higher than 32 bits, truncate the left.
		if #h2 > 32 then
			h2 = string.sub(h2,(#h2-32)+1,#h2)
		end
		h3 = h3 -badd- d
		-- If higher than 32 bits, truncate the left.
		if #h3 > 32 then
			h3 = string.sub(h3,(#h3-32)+1,#h3)
		end
		h4 = h4 -badd- e
		-- If higher than 32 bits, truncate the left.
		if #h4 > 32 then
			h4 = string.sub(h4,(#h4-32)+1,#h4)
		end
	end
	return Bin2Hex(h0)..Bin2Hex(h1)..Bin2Hex(h2)..Bin2Hex(h3)..Bin2Hex(h4)
end

function hmac(algorithm, key, message)
	assert(type(algorithm) == "table", "algorithm must be a table for a specific algorithm")
	assert(type(key) == "string", "key passed to hmac should be a string")
	assert(type(key) == "string", "text passed to hmac should be a string")
	local hash = algorithm.computehash
	local blocksize = algorithm.blocksize
	if #key > blocksize then
		key = Hex2String(hash(String2Bin(key),true))
	end
	if #key < blocksize then
		key = key..string.rep(string.char(0x0),blocksize-#key)
	end
	o_key_pad = Hex2String(String2Hex(key) -hxor- String2Hex(string.rep(string.char(0x5c),blocksize)))
	i_key_pad = Hex2String(String2Hex(key) -hxor- String2Hex(string.rep(string.char(0x36),blocksize)))
	return hash(o_key_pad..Hex2String(hash(i_key_pad..message)))
end